﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace Constellation2XML
{
	enum XMLElement
	{
		ConstellationList,
		ConstellationSegment,
		Star,
		Constellation,
		Name,
	}

	enum XMLAttribute
	{
		RightAscension,
		Declination,
		Abbreviation,
	}

	internal static class ReaderExtensions
	{
		internal static void ForEachData(this BinaryReader reader, Action<int> body)
		{
			int count = reader.ReadInt32();
			for (int i = 0; i < count; i++)
				body(i);
		}
	}

	internal static class WriterExtensions
	{
		const string NS = "urn:denebsoft-skymap-constellation";

		internal static void WriteDocumentElement(this XmlWriter writer, XMLElement name, Action body)
		{
			writer.WriteStartDocument();
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteEndElement();
			writer.WriteEndDocument();
		}

		internal static void WriteElement(this XmlWriter writer, XMLElement name, Action body)
		{
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteEndElement();
		}

		internal static void WriteElement(this XmlWriter writer, XMLElement name, string value, Action body)
		{
			if (value == " ") return;
			writer.WriteStartElement(name.ToString(), NS);
			body();
			writer.WriteString(value);
			writer.WriteEndElement();
		}

		internal static void WriteStartElement(this XmlWriter writer, XMLElement name)
		{
			writer.WriteStartElement(name.ToString(), NS);
		}

		internal static void WriteAttribute(this XmlWriter writer, XMLAttribute name, double value)
		{
			writer.WriteAttributeString(name.ToString(), XmlConvert.ToString(value));
		}

		internal static void WriteAttribute(this XmlWriter writer, XMLAttribute name, string value)
		{
			writer.WriteAttributeString(name.ToString(), value);
		}

		internal static void WriteAttributeLanguage(this XmlWriter writer, string lang)
		{
			writer.WriteAttributeString("xml", "lang", null, lang);
		}

		internal static void Write(this XmlWriter writer, string value)
		{
			writer.WriteString(value);
		}
	}

	class Program
	{
		static void Main(string[] args)
		{
			string ifn = args.Length == 0 || Path.GetExtension(args[0]) == ".xml" ? "constellation.dat" : args[0];
			string ofn = args.Length == 1 && Path.GetExtension(args[0]) == ".xml" ? args[0] : args.Length <= 1 ? "constellation.xml" : args[1];

			if (args.Length > 2 || Path.GetExtension(ifn) != ".dat" || Path.GetExtension(ofn) != ".xml" ||
				args.Length > 1 && Path.GetExtension(args[0]) == ".xml")
			{
				Console.WriteLine("usage: {0} [constellation.dat] [constellation.xml]", Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().Location));
				return;
			}

			using (var ifs = new FileStream(ifn, FileMode.Open))
			using (var reader = new BinaryReader(ifs))
			using (var writer = XmlWriter.Create(ofn, new XmlWriterSettings()
			{
				Encoding = Encoding.UTF8,
				Indent = true,
				IndentChars = "\t",
				OmitXmlDeclaration = false
			}))
			{
				writer.WriteDocumentElement(XMLElement.ConstellationList, () =>
				{
					double? LastRA = null;
					double? LastDE = null;

					reader.ForEachData(i =>
					{
						double RA1 = reader.ReadDouble();
						double RA2 = reader.ReadDouble();
						double DE1 = reader.ReadDouble();
						double DE2 = reader.ReadDouble();
						if (LastRA != RA1 || LastDE != DE1)
						{
							if (LastRA.HasValue)
								writer.WriteEndElement(); //ConstellationSegment
							writer.WriteStartElement(XMLElement.ConstellationSegment);

							writer.WriteElement(XMLElement.Star, () =>
							{
								writer.WriteAttribute(XMLAttribute.RightAscension, RA1);
								writer.WriteAttribute(XMLAttribute.Declination, DE1);
							});
						}

						writer.WriteElement(XMLElement.Star, () =>
						{
							writer.WriteAttribute(XMLAttribute.RightAscension, RA2);
							writer.WriteAttribute(XMLAttribute.Declination, DE2);
						});
						LastRA = RA2; LastDE = DE2;
					});
					if (LastRA.HasValue)
						writer.WriteEndElement(); //ConstellationSegment

					reader.ForEachData(i =>
					{
						writer.WriteElement(XMLElement.Constellation, () =>
						{
							double RA = reader.ReadDouble();
							double Dec = reader.ReadDouble();
							string ShortName = reader.ReadString();
							string FullName = reader.ReadString();
							writer.WriteAttribute(XMLAttribute.Abbreviation, ShortName);
							writer.WriteAttribute(XMLAttribute.RightAscension, RA);
							writer.WriteAttribute(XMLAttribute.Declination, Dec);

							writer.WriteElement(XMLElement.Name, FullName, () =>
							{
								writer.WriteAttributeLanguage("la");
							});
						});
					});
				});
			}
		}
	}
}
